// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.editorconfig.language.formatting

import com.intellij.formatting.FormattingModel
import com.intellij.formatting.FormattingModelBuilder
import com.intellij.formatting.FormattingModelProvider
import com.intellij.formatting.SpacingBuilder
import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleSettings
import org.editorconfig.language.EditorConfigLanguage
import org.editorconfig.language.psi.EditorConfigElementTypes

class EditorConfigFormattingModelBuilder : FormattingModelBuilder {
  override fun createModel(element: PsiElement, settings: CodeStyleSettings): FormattingModel {
    val spacingBuilder = createSpacingBuilder(settings)
    val commonSettings = settings.getCommonSettings(EditorConfigLanguage)
    val shouldAlignSeparators = commonSettings.ALIGN_GROUP_FIELD_DECLARATIONS
    val root = EditorConfigFormattingBlock(element.node, spacingBuilder, shouldAlignSeparators)
    return FormattingModelProvider.createFormattingModelForPsiFile(element.containingFile, root, settings)
  }

  private fun createSpacingBuilder(settings: CodeStyleSettings): SpacingBuilder {
    val commonSettings = settings.getCommonSettings(EditorConfigLanguage)
    val beforeComma = commonSettings.SPACE_BEFORE_COMMA
    val afterComma = commonSettings.SPACE_AFTER_COMMA
    val beforeColon = commonSettings.SPACE_BEFORE_COLON
    val afterColon = commonSettings.SPACE_AFTER_COLON
    val aroundSeparator = commonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS

    return SpacingBuilder(settings, EditorConfigLanguage)
      .around(EditorConfigElementTypes.SEPARATOR).strictSpaces(aroundSeparator)
      .before(EditorConfigElementTypes.COMMA).strictSpaces(beforeComma)
      .after(EditorConfigElementTypes.COMMA).strictSpaces(afterComma)
      .around(EditorConfigElementTypes.DOT).strictSpaces(false)
      .before(EditorConfigElementTypes.COLON).strictSpaces(beforeColon)
      .after(EditorConfigElementTypes.COLON).strictSpaces(afterColon)
      .around(EditorConfigElementTypes.LINE_COMMENT).spaces(1)
      .around(EditorConfigElementTypes.OPTION).lineBreakInCode()
      .around(EditorConfigElementTypes.ROOT_DECLARATION).blankLines(1)
      .around(EditorConfigElementTypes.SECTION).blankLines(1)
  }

  private fun SpacingBuilder.RuleBuilder.strictSpaces(needSpace: Boolean) = spacing(needSpace.toInt(), needSpace.toInt(), 0, false, 0)
  private fun Boolean.toInt() = if (this) 1 else 0
}
